package main

import (
	"bufio"
	"crypto/md5"
	"database/sql"
	"fmt"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)
//数据库全局句柄
var db *sql.DB
//登录模块，用于接收数据库查询的结果
type User struct{
	Username string
	Password string
}
func main(){
	//连接mysql数据库
	user := "root"
	passwd := "AN78zm9W.AK789"
	ipaddr_port := "localhost:3306"
	databs := "goblog"
	net := "tcp"
	mysql_url := fmt.Sprintf("%s:%s@%s(%s)/%s",user,passwd,net,ipaddr_port,databs)
	db , err := sql.Open("mysql",mysql_url)
	if err != nil{
		fmt.Println(err.Error())
	}
	//测试mysql是否连接成功
	PingErr := db.Ping()
	if PingErr != nil{
		fmt.Println(PingErr.Error())
	}

	//注册一个路由
	router := gin.Default()
	//加载所有渲染模板和图像
	router.StaticFS("/images",http.Dir("./images"))
	router.StaticFS("/layui",http.Dir("./layui-v2.6.8"))
	router.LoadHTMLGlob("templates/*")
	//处理http请求
	router.GET("/", func(c *gin.Context) {
		rows , _ := db.Query("select username,artical_name,description,title from gouser_blog order by create_time desc limit 5 ")
		username := ""
		artical_name := ""
		description := ""
		title := ""
		content := ""
		for rows.Next(){
			rows.Scan(&username,&artical_name,&description,&title)
			content += "<a href=\"./"+artical_name+"\"><div class=\"layui-card\">\n                <div class=\"layui-card-header\" style=\"background-color:orange\">"+title+" 作者:"+username+"</div>\n                <div class=\"layui-card-body\">\n                    \n                    <br>\n                    <div>\n                       "+description+"                 </div>\n </div>\n            </div></a><br><br>"
		}
		rows , _ = db.Query("select title,artical_name from gouser_blog order by star desc limit 5")
		title = ""
		artical_name = ""
		list := ""
		for rows.Next(){
			rows.Scan(&title,&artical_name)
			list += "<div class=\"layui-card\">\n                            <div class=\"layui-card-body\">\n                            <a href=\"./"+artical_name+"\">    "+title+"\n </a>                           </div>\n                        </div>"
		}
		c.HTML(http.StatusOK,"index.html",gin.H{"content":template.HTML(content),"list":template.HTML(list)})
	})
	router.GET("/login", func(c *gin.Context) {
		c.HTML(http.StatusOK,"login.html",gin.H{})
	})
	router.POST("/login", func(c *gin.Context) {
		//获取表单数据
		username := c.PostForm("username")
		password := c.PostForm("password")
		//如果用户名或密码为空，返回重新渲染的模板，提示问题
		if username == "" || password == "" {
			c.HTML(http.StatusOK,"login.html",gin.H{"message":"用户名或密码不能为空"})
			return
		}
		//将获取的明文密码进行md5加密，推送到数据库中查询
		data := []byte(password)
		has := md5.Sum(data)
		md5_password := fmt.Sprintf("%x", has)
		//使用占位标记防止SQL注入.
		row  := db.QueryRow("select count(*) from gouser where username=? and password=?", username, md5_password)
		num := ""
		err = row.Scan(&num)
		//判断输入的账号密码是否正确，如果错误重新渲染模板，提示问题
		if num == "0"{
			//登录失败，重新渲染模板
			c.HTML(http.StatusOK,"login.html",gin.H{"status":"true"})
			return
		}
		//启用httponly，防止js读取cookie
		c.SetCookie("username",username,3600,"/","localhost",false,true)
		c.SetCookie("password",md5_password,3600,"/","localhost",false,true)
		//登录成功跳转到管理界面
		c.Redirect(http.StatusMovedPermanently,"/admin")
	})
	router.GET("/admin", func(c *gin.Context) {
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//将登录日志写进log
		ipaddress , err := c.RemoteIP()
		if err{
			log.Fatal("获取远程ip错误")
		}
		username , error := c.Cookie("username")
		if error != nil{
			fmt.Println("Cookie err")
		}
		t := time.Now()
		year := t.Year();
		month := t.Month();
		day := t.Day();
		hour := t.Hour();
		minute := t.Minute();
		second := t.Second()
		t = time.Date(year,month,day,hour,minute,second,0,time.UTC)
		logintime := t.String()
		db.QueryRow("insert into gouser_log(username,ipaddress,logintime) values(?,?,?)",username,ipaddress,logintime)
		c.HTML(http.StatusOK,"admin.html",gin.H{})
	})
	router.GET("/admin/center", func(c *gin.Context) {
		//检查登录状态
		b := CheckLogin(c,db)
		fmt.Println(b)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//获取最近的五条日志信息
		rows , _ := db.Query("select * from gouser_log  order by logintime desc limit 5")
		usern := ""
		ipaddr := ""
		logintime := ""
		logstr := ""
		for rows.Next(){
			rows.Scan(&usern,&ipaddr,&logintime)
			logstr += "<tr>"+"<td>"+usern+"</td>"+"<td>"+ipaddr+"</td>"+"<td>"+logintime+"</td>"+"</tr>"
		}
		username , _ := c.Cookie("username")
		private := "超级管理员"
		if username != "admin"{
			private = "管理员"
		}
		c.HTML(http.StatusOK,"center.html",gin.H{"logmsg":template.HTML(logstr),"username":username,"private":private})
	})
	router.GET("/logout", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		c.Redirect(http.StatusMovedPermanently,"/login")
	})
	router.GET("/admin/manage", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//检查用户权限
		username , _ := c.Cookie("username")
		if username != "admin"{
			c.String(http.StatusUnauthorized,"你无权访问这个模块")
			return
		}
		usernum := "0"
		row := db.QueryRow("select count(*) from gouser")
		row.Scan(&usernum)
		c.HTML(http.StatusOK,"manage.html",gin.H{"usernum":usernum})
	})
	router.POST("/admin/manage/showusers", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//检查用户权限
		username , _ := c.Cookie("username")
		if username != "admin"{
			c.String(http.StatusUnauthorized,"你无权访问这个模块")
			return
		}
		rows , _ := db.Query("select username from gouser")
		users := ""
		usernum := 0
		for rows.Next(){
			user := ""
			rows.Scan(&user)
			users += user+"<br>"
			usernum += 1
		}
		user_num := strconv.Itoa(usernum)
		c.HTML(http.StatusOK,"manage.html",gin.H{"usernum":user_num,"users":template.HTML(users)})
	})
	router.POST("/admin/manage/adduser", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//检查用户权限
		username , _ := c.Cookie("username")
		if username != "admin"{
			c.String(http.StatusUnauthorized,"你无权访问这个模块")
			return
		}
		username = c.PostForm("username")
		password := c.PostForm("password")
		if username == "" || password == ""{
			c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"警告","adduser_content":"用户名或密码不能为空"})
			return
		}
		row := db.QueryRow("select count(*) from gouser where username=?",username)
		flag := ""
		row.Scan(&flag)
		if flag != "0"{
			c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"警告","adduser_content":"该用户已存在"})
			return
		}
		//将获取的明文密码进行md5加密，推送到数据库中查询
		data := []byte(password)
		has := md5.Sum(data)
		md5_password := fmt.Sprintf("%x", has)
		row = db.QueryRow("insert into gouser(username,password) values(?,?)",username,md5_password)
		c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"成功","adduser_content":"创建账户成功"})
		return
	})
	router.POST("/admin/manage/deleteuser", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//检查用户权限
		username , _ := c.Cookie("username")
		if username != "admin"{
			c.String(http.StatusUnauthorized,"你无权访问这个模块")
			return
		}
		username = c.PostForm("username")
		if username == "admin"{
			c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"警告","adduser_content":"超级管理员不允许删除"})
			return
		}
		flag := ""
		row := db.QueryRow("select count(*) from gouser where username=?",username)
		row.Scan(&flag)
		if flag != "1"{
			c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"警告","adduser_content":"用户不存在"})
			return
		}
		db.QueryRow("delete from gouser where username=?",username)
		c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"成功","adduser_content":"删除用户成功"})

	})
	router.POST("/admin/manage/resetpassword", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		//检查用户权限
		username , _ := c.Cookie("username")
		if username != "admin"{
			c.String(http.StatusUnauthorized,"你无权访问这个模块")
			return
		}
		username = c.PostForm("username")
		password := c.PostForm("password")
		//将获取的明文密码进行md5加密，推送到数据库中查询
		data := []byte(password)
		has := md5.Sum(data)
		md5_password := fmt.Sprintf("%x", has)
		flag := ""
		row := db.QueryRow("select count(*) from gouser where username=?",username)
		row.Scan(&flag)
		if flag != "1"{
			c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"警告","adduser_content":"用户不存在"})
			return
		}
		db.QueryRow("update gouser set password=? where username=?",md5_password,username)
		c.HTML(http.StatusMovedPermanently,"manage.html",gin.H{"adduser_status":"true","adduser_title":"成功","adduser_content":"密码已重置"})
	})
	router.GET("/admin/addartical", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		c.HTML(http.StatusOK,"addartical.html",gin.H{})
	})
	router.POST("/admin/addartical", func(c *gin.Context) {
		//退出账号,检测登录状态
		//检查登录状态
		b := CheckLogin(c,db)
		if b == false{
			c.Redirect(http.StatusMovedPermanently,"/login")
		}
		title := c.PostForm("title")
		content := c.PostForm("content")
		t := time.Now()
		year := t.Year();
		month := t.Month();
		day := t.Day();
		hour := t.Hour();
		minute := t.Minute();
		second := t.Second()
		t = time.Date(year,month,day,hour,minute,second,0,time.UTC)
		data := []byte(t.GoString())
		has := md5.Sum(data)
		username , _ := c.Cookie("username")
		md5_blog_title := fmt.Sprintf("%x", has)
		b_f , _ := os.Open("./templates/blog.html")
		reader := bufio.NewReader(b_f)
		html := ""
		for {
			temp , err := reader.ReadString('\n')
			if err == io.EOF{
				break
			}
			html += temp
		}
		new_html := strings.Replace(html,"{{.title}}",title,-1)
		new_html = strings.Replace(new_html,"{{.username}}",username,-1)
		new_html = strings.Replace(new_html,"{{.content}}",content,-1)
		f , _ := os.Create("./templates/"+md5_blog_title+".html")
		fmt.Println(new_html)
		writer := bufio.NewWriter(f)
		writer.WriteString(new_html)
		writer.Flush()
		description := []byte(content)
		db.QueryRow("insert into gouser_blog(username,create_time,artical_name,description,title) values(?,?,?,?,?)",username,t,md5_blog_title,string(description[0:19]),title)
		c.HTML(http.StatusMovedPermanently,"addartical.html",gin.H{"adduser_status":"true","adduser_title":"成功","adduser_content":"文章已发布"})
	})
	router.GET("/blog", func(c *gin.Context) {
		c.HTML(http.StatusOK,"blog.html",gin.H{})
	})
	router.GET("/:title", func(c *gin.Context) {
		path := c.Param("title")
		//获取文章阅读数
		row := db.QueryRow("select star from gouser_blog where artical_name=?",path)
		star := ""
		row.Scan(&star)
		if star == "NULL"{
			star = "0"
		}
		temp_star , _ := strconv.Atoi(star)
		temp_star += 1
		star = strconv.Itoa(temp_star)
		fmt.Println(star)
		db.QueryRow("update gouser_blog set star=? where artical_name=?",star,path)
		c.HTML(http.StatusOK,path+".html",gin.H{})
	})
	//启动路由
	err = router.Run(":8000")
	if err != nil{
	}

}

//检查Cookie是否设置,设置返回true，未设置返回fasle
func CheckLogin(c *gin.Context,db *sql.DB) bool{
	//检查Cookie是否正确
	var user User
	username , _ := c.Cookie("username")
	password , _ := c.Cookie("password")
	row  := db.QueryRow("select * from gouser where username=? and password=?", username, password)
	row.Scan(&user.Username,&user.Password)
	//未设置Cookie，重定向到登录界面
	if user.Username == ""{
		return false
	}
	return true
}

